﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace A2DFramework.ValidationService
{
    public class ValidatorExecutor : IValidatorExecutor
    {
        private string scene = "";

        public List<BrokenRule> IsValid<T>(T o, string scene = "", bool only1Level = false)
        {
            this.scene = scene;
            return IsValid(typeof(T), o, only1Level);
        }

        private List<BrokenRule> IsValid(Type t, object o, bool only1Level)
        {
            List<BrokenRule> errors = new List<BrokenRule>();

            var descriptor = GetTypeDescriptor(t);

            foreach (PropertyDescriptor propertyDescriptor in descriptor.GetProperties())
            {
                foreach (var validationAttribute in propertyDescriptor.Attributes.OfType<ValidationAttribute>())
                {
                    RuleValidationAttribute baseAttr = validationAttribute as RuleValidationAttribute;
                    if (baseAttr != null)
                    {
                        //支持场景验证
                        if (baseAttr.RuleName.Equals(scene, StringComparison.OrdinalIgnoreCase))
                        {
                            ValidateProperty(o, errors, propertyDescriptor, validationAttribute);
                        }
                    }
                    else
                    {
                        //不支持场景的验证
                        ValidateProperty(o, errors, propertyDescriptor, validationAttribute);
                    }
                }
            }

            if (!only1Level)
            {
                if (o.GetType().IsClass && !o.GetType().Equals(typeof(string)))
                {
                    foreach (var p in o.GetType().GetProperties())
                    {
                        ParameterInfo[] pInfos = p.GetIndexParameters();
                        if (pInfos != null && pInfos.Count() > 0)
                        {
                            int rowIndex = 0;
                            foreach (var pInfo in pInfos)
                            {
                                object pValue = null;
                                while (true)
                                {
                                    try
                                    {
                                        pValue = p.GetValue(o, new object[] { rowIndex++ });
                                    }
                                    catch (Exception ex)
                                    {
                                        break;
                                    }
                                    if (pValue != null)
                                    {
                                        List<BrokenRule> pErrors = IsValid(p.PropertyType, pValue, only1Level);
                                        errors.AddRange(pErrors);
                                    }
                                }
                            }
                        }
                        else
                        {
                            object pValue = p.GetValue(o, null);
                            if (pValue != null)
                            {
                                if (!p.PropertyType.IsValueType || p.PropertyType.IsEnum)
                                {
                                    List<BrokenRule> pErrors = IsValid(p.PropertyType, pValue, only1Level);
                                    errors.AddRange(pErrors);
                                }
                            }
                        }
                    }
                }
            }

            return errors;
        }

        private void ValidateProperty(object o, List<BrokenRule> errors, PropertyDescriptor propertyDescriptor, ValidationAttribute validationAttribute)
        {
            object obj = propertyDescriptor.GetValue(o);
            ValidationContext ctx = new ValidationContext(o, null, null);
            ValidationResult result = validationAttribute.GetValidationResult(obj, ctx);
            if (result != ValidationResult.Success)
            {
                BrokenRule error = new BrokenRule();
                error.Key = propertyDescriptor.Name;
                error.Message = validationAttribute.FormatErrorMessage(propertyDescriptor.Name);
                errors.Add(error);
            }
        }
        private ICustomTypeDescriptor GetTypeDescriptor(Type type)
        {
            return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
        }
    }
}
